from PyQt4.QtCore import QPointF,QSizeF,QObject,Qt,QRectF
from PyQt4.QtGui import QTransform,QGraphicsTextItem
from KApp import KConstants as vm
import numpy as np
import collections

class KGraphicsSceneSubViewModel(QObject):
	'''This class represnt the model of a subview (such as price view) of the scene,
	it helps KGraphicsScene to handle coordiante translation and positioning of each view
	each subview should have one instance of this class
	'''

	
	def __init__(self,parent,geometrydic):
		QObject.__init__(self,parent)
		self.GeometryDict=geometrydic
		self.GeometryDict[vm.DictType.RectsList]={}
		self.dispitemlist=[] # maintain all display items attached to this viewmodel		
		self.OuterInnerMargin=10 # the margin between outer rect and innter rect
		self.AxisMargin=40 # the margin to decide how much space each axis has
		self.ChildViewModel=collections.OrderedDict() # maintain the child view model list
	
	
	def AddChildModel(self,childmodel):

		viewname=childmodel.GetViewName()
		self.ChildViewModel[viewname]=childmodel
		
		
	def SetRectAttr(self,rectname,rectattr,attrcontent):
		if rectname not in self.GeometryDict[vm.DictType.RectsList]:
			self.GeometryDict[vm.DictType.RectsList][rectname]={}
		self.GeometryDict[vm.DictType.RectsList][rectname][rectattr]=attrcontent
		
	def GetRectAttr(self,rectname,rectattr):
		if rectname not in self.GeometryDict[vm.DictType.RectsList].keys():
			return None
		if rectattr not in self.GeometryDict[vm.DictType.RectsList][rectname].keys():
			return None
		return self.GeometryDict[vm.DictType.RectsList][rectname][rectattr]
	
	def IsCrossScene(self):
		#tell if the view is cross the whole scene or just part of it
		return (True if self.GetSizePolicy()==0 else False)
	
	def GetViewName(self):
		return self.GeometryDict[vm.DictType.Name]
	
	def GetSizePolicy(self):
		return self.GeometryDict[vm.DictType.SizePolicy]
	
	def SetSizePolicy(self,num):
		self.GeometryDict[vm.DictType.SizePolicy]=num
		
	def GetViewRect(self,RectType=vm.RectType.OuterBound):
		return self.GetRectAttr(RectType,vm.DictType.Bound)
	
	# set the outerbound of the view
	def SetViewRect(self,rect):
		# set the outerrect: which is the acutal border of the view
		rect=QRectF(rect)
		self.GeometryDict[vm.RectType.OuterBound]=rect
		self.SetRectAttr(vm.RectType.OuterBound,vm.DictType.Bound,rect)
		# set the innerrect: which is the acutal border of the view (including axis and content bound)
		newrect=rect.adjusted(self.OuterInnerMargin,self.OuterInnerMargin,-self.OuterInnerMargin,-self.OuterInnerMargin) #shrink
		self.SetRectAttr(vm.RectType.InnerBound,vm.DictType.Bound,newrect)
	
		offset=self.AxisMargin
		# set content bound: which is the content draw area
		trect=QRectF(QPointF(newrect.left()+offset,newrect.top()+offset),QSizeF(newrect.width()-2*offset,newrect.height()-2*offset))		
		self.SetRectAttr(vm.RectType.ContentBound,vm.DictType.Bound,trect)		
		# set the Top Axis
		trect=QRectF(QPointF(newrect.left()+offset,newrect.top()),QSizeF(newrect.width()-2*offset,offset))
		self.SetRectAttr(vm.RectType.XAxisTop,vm.DictType.Bound,trect)	
		# set the Bottom Axis
		trect=QRectF(QPointF(newrect.left()+offset,newrect.bottom()-offset),QSizeF(newrect.width()-2*offset,offset))
		self.SetRectAttr(vm.RectType.XAxisBottom,vm.DictType.Bound,trect)	
		# set the Left Axis
		trect=QRectF(QPointF(newrect.left(),newrect.top()+offset),QSizeF(offset,newrect.height()-2*offset))
		self.SetRectAttr(vm.RectType.YAxisLeft,vm.DictType.Bound,trect)	
		# set the Rigth Axis
		trect=QRectF(QPointF(newrect.right()-offset,newrect.top()+offset),QSizeF(offset,newrect.height()-2*offset))
		self.SetRectAttr(vm.RectType.YAxisRight,vm.DictType.Bound,trect)	
		
	def UpdateLogicMap(self,stockbobj,dispitems):

		# should be called by GraphicsScene whenever the stockobject gets changed: (moved,scaled,or so)
		
		rangelist=[]
		for each_item in dispitems:
			trange=each_item.GetModel().GetLogicRange()
			if trange:
				rangelist.append([trange[0][0],trange[0][1],trange[1][0],trange[1][1]])
		nrangelist=np.array(rangelist)
		xdatarange=[np.min(nrangelist[:,0]),np.max(nrangelist[:,1])]
		ydatarange=[np.min(nrangelist[:,2]),np.max(nrangelist[:,3])]
		
		if self.GeometryDict[vm.DictType.Name]=='VolView':
			if ydatarange[1]<10:
				test='ni'

		for rect_name in self.GeometryDict[vm.DictType.RectsList].keys():			
			rect_obj=self.GeometryDict[vm.DictType.RectsList][rect_name]
			mrect=rect_obj[vm.DictType.Bound]
			if rect_name==vm.RectType.ContentBound:
				txdatarange=xdatarange
				tydatarange=ydatarange
				#(txdatarange,tydatarange)=stockbobj.CurWindowRange(self.GeometryDict[vm.DictType.YRange])
				#print('x: %.2f y: %.2f tx: %.2f ty: %.2f'%(ydatarange[0],ydatarange[1],tydatarange[0],tydatarange[1]))
			else:
				#(xdatarange,temp)=stockbobj.CurWindowRange(self.GeometryDict[vm.DictType.YRange])
				txdatarange=xdatarange
				tydatarange=[mrect.top(),mrect.bottom()]
			
			# building transforms of the mapping between logic and scene coordinates
			self.__BuildTransform(rect_obj,mrect,txdatarange,tydatarange)
				

			
	def __BuildTransform(self,rect_obj,mrect,xdatarange,ydatarange):
		'''building transforms of the mapping between logic and scene coordinates
		rect_obj: the dict to store transforms objects, each view will have a rect_obj in GeometryDict
		mrect: the bounding rect in terms of scene coordindate
		xdatarange: the range of logical x
		ydatarange: the range of logical y
		'''
		LogicWidth=xdatarange[1]-xdatarange[0]
		LogicHeight=ydatarange[1]-ydatarange[0]
	
		# Build Matrix for map logic cordinates to screen cordinates
		mtrans=QTransform()
		#build matrix in the revers order as the actual coordiants translates
		mtrans.translate(mrect.left(),mrect.top())	# step 4): move back to actual position in screening coordinates system
		mtrans.translate(0,mrect.height())	# step 3): compensate for the yoffset introduced in flip
		mtrans.scale(mrect.width()*1.0/LogicWidth,-mrect.height()*1.0/LogicHeight) # step 2): scale to screen coordinates ratio,flip yaxis
		mtrans.translate(-xdatarange[0],-ydatarange[0]) # step 1): to (0,0) of logic coordiantes system
		rect_obj[vm.DictType.LogicCor2ScreenT]=mtrans
		rect_obj[vm.DictType.ScreenCor2Logic]=mtrans.inverted()[0]
		
		# Build Matrix for map logic cordinates to screen cordinates
		mtrans=QTransform()
		mtrans.scale(mrect.width()*1.0/LogicWidth,mrect.height()*1.0/LogicHeight) 
		rect_obj[vm.DictType.LogicSize2ScreenT]=mtrans
		rect_obj[vm.DictType.ScreenSize2Logic]=mtrans.inverted()[0]

		
	def GetViewScreenRect(self,RectType):
		# convinent class to get rect in terms of screening coordinate
		mrect=self.GetViewRect(RectType)
		newcordinates=self.mapLogiceCordToScene(RectType,[mrect.left(),mrect.right()],[mrect.top(),mrect.bottom()])
		NewRect=QRectF(QPointF(mrect.left(),mrect.top()),QPointF(mrect.right(),mrect.bottom()))
		return NewRect
	
		
	def mapLogiceCordToScene(self,RectType,xlist,ylist=[]):
		# map the logic coordinates(which is index of symol price
		return self.__mapBetweenCoordinates(RectType,vm.DictType.LogicCor2ScreenT,xlist,ylist)
	def mapLogiceSizeToScene(self,RectType,widethlist=[],heightlist=[]):
		# map the logic coordinates(which is index of symol price
		return self.__mapBetweenCoordinates(RectType,vm.DictType.LogicSize2ScreenT,widethlist,heightlist)
	
	def mapScreenCordToLogic(self,RectType,xlist,ylist=[]):
		return self.__mapBetweenCoordinates(RectType,vm.DictType.ScreenCor2Logic,xlist,ylist)
	def mapScreenSizeToLogic(self,RectType,xlist,ylist=[]):
		return self.__mapBetweenCoordinates(RectType,vm.DictType.ScreenSize2Logic,xlist,ylist)
	
	def __mapBetweenCoordinates(self,RectType,MatrixType,xlist,ylist=[]):
		mtrans=self.GetRectAttr(RectType,MatrixType)
		newxlist=[mtrans.map(each_x,0)[0] for each_x in xlist]
		newylist=[mtrans.map(0,float(each_y))[1] for each_y in ylist]
		return newxlist,newylist
	

